#include <cstring>
#include <iostream>
#include <stdexcept>
#include "xstdlib.h"
#include "xstring.h"
using namespace std;

char* safeStrdup(const char* s)
    {
    if(s==NULL)
	{
	cerr << "safeStrdup: null.\n";
	std::exit(EXIT_FAILURE);
	}
    return safeStrndup(s,strlen(s));
    }

char* safeStrndup(const char* s,size_t len)
    {
    if(s==NULL)
	{
	cerr << "safeStrdup: null.\n";
	std::exit(EXIT_FAILURE);
	}
    char* cp=(char*)safeMalloc((len+1)*sizeof(char));
    strncpy(cp,s,len);
    cp[len]=0;
    return cp;
    }

CharSequence::CharSequence()
    {
    }
CharSequence::~CharSequence()
    {
    }
char CharSequence::operator[](size_type idx) const
    {
    return at(idx);
    }
void CharSequence::print(std::ostream& out) const
    {
    size_type L=size();
    for(size_type i=0;i< L;++i)
	{
	char c=at(i);
	out.write(&c,1);
	}
    }
bool CharSequence::equals(const CharSequence* cp) const
    {
    if(this==cp) return true;
    if(size()!=cp->size()) return false;
    for(size_type i=0;i<size();++i)
	{
	if(at(i)!=cp->at(i)) return false;
	}
    return true;
    }
bool CharSequence::equalsIgnoreCase(const CharSequence* cp) const
    {
    if(this==cp) return true;
    if(size()!=cp->size()) return false;
    for(size_type i=0;i<size();++i)
	{
	if(std::toupper(at(i))!=std::toupper(cp->at(i)))
	    {
	    return false;
	    }
	}
    return true;
    }
int CharSequence::compareTo(const CharSequence* cp) const
    {
    CharSequence::size_type i1=0;
    CharSequence::size_type i2=0;
    while( i1 <size() &&
	   i2< cp->size()
	  )
	{
	int d= at(i1) - cp->at(i2);
	if(d!=0) return d;
	i1++;
	i2++;
	}
    if(i1==size())
	{
	if(i2==cp->size()) return 0;
	return -1;
	}
    return 1;
    }
CharSequence::size_type CharSequence::find(const CharSequence* other,CharSequence::size_type beg) const
    {
    CharSequence::size_type i=0;
    while(beg+other->size()<=size())
	{
	for(i=0;i< other->size();++i)
	    {
	    if(at(beg+i)!=other->at(i)) break;
	    }
	if(i==other->size()) return beg;
	++beg;
	}
    return npos;
    }
bool CharSequence::startsWith(const CharSequence* cp) const
    {
    if(cp->size()>size()) return false;
    SubStringSequence slice(this,0,cp->size());
    return slice.equals(cp);
    }

bool CharSequence::endsWith(const CharSequence* cp) const
    {
    if(cp->size()>size()) return false;
    SubStringSequence slice(this,size()-cp->size(),cp->size());
    return slice.equals(cp);
    }
std::string CharSequence::toString() const
    {
    std::ostringstream os;
    for(size_type i=0;i< size();++i)
	{
	os << at(i);
	}
    return os.str();
    }

std::ostream& operator<<(std::ostream& os, const CharSequence* seq)
    {
    seq->print(os);
    return os;
    }


DelegateCharSequence::DelegateCharSequence()
    {

    }

DelegateCharSequence::~DelegateCharSequence()
    {

    }


DelegateCharSequence::size_type
DelegateCharSequence::size() const
    {
    return delegate()->size();
    }

char DelegateCharSequence::at(size_type idx) const
    {
    return delegate()->at(idx);
    }


SubStringSequence::SubStringSequence(const CharSequence* delegate,size_type pos,size_type len):
	    _delegate(delegate),
	    _pos(pos),_len(len)
    {
    }
SubStringSequence::SubStringSequence(const CharSequence* delegate):
	    _delegate(delegate),
	    _pos(0),_len(delegate->size())
    {
    }
SubStringSequence::~SubStringSequence() {}
CharSequence::size_type SubStringSequence::size() const
    {
    return _len;
    }
char SubStringSequence::at(size_type idx) const
    {
#ifndef NDEBUG
    assert(idx<size());
#endif
    return _delegate->at(_pos+idx);
    }





AbstractCString::AbstractCString(size_t len):_len(len)
    {
    }
AbstractCString::~AbstractCString()
    {
    }

AbstractCString::size_type AbstractCString::size() const
    {
    return _len;
    }
void AbstractCString::print(std::ostream& out) const
    {
    out.write(data(),size());
    }



CStringWapper::CStringWapper(const char* s,size_t len):AbstractCString(len),_s(s)
	    {
	    }
CStringWapper::CStringWapper(const char* s):AbstractCString(std::strlen(s)),_s(s)
	{
	}
CStringWapper::~CStringWapper() {}
const char* CStringWapper::data() const
	{
	return _s;
	}
char CStringWapper::at(size_type idx) const
	{
#ifndef NDEBUG
    assert(idx<size());
#endif
	return _s[idx];
	}


CStringCopy::CStringCopy(const char* s,size_t len):AbstractCString(len)
    {
    _s=new char[len+1];
    std::memcpy((void*)_s,s,sizeof(char)*len);
    _s[len]='\0';
    }
CStringCopy::CStringCopy(const char* s):AbstractCString(std::strlen(s))
    {
    _s=new char[_len+1];
    std::memcpy((void*)_s,s,sizeof(char)*_len);
    _s[_len]='\0';
    }
CStringCopy::CStringCopy(const CStringCopy& cp):AbstractCString(cp.size())
    {
    _s=new char[cp.size()+1];
    std::memcpy((void*)_s,cp.c_str(),sizeof(char)*cp.size());
    _s[cp.size()]='\0';
    }
CStringCopy::CStringCopy(const CharSequence* cp):AbstractCString(cp->size())
    {
    _s=new char[cp->size()+1];
    for(size_t i=0;i< cp->size();++i)
	{
	_s[i]=cp->at(i);
	}
    _s[cp->size()]=0;
    }

CStringCopy::~CStringCopy()
    {
    delete [] _s;
    }

CStringCopy& CStringCopy::operator=(const CStringCopy& cp)
    {
    if(this!=&cp)
	{
	delete [] _s;
	_s=new char[cp.size()+1];
	std::memcpy((void*)_s,cp.c_str(),sizeof(char)*cp.size());
	_s[cp.size()]=0;
	}
    return *this;
    }

const char* CStringCopy::c_str() const
    {
    return _s;
    }

const char* CStringCopy::data() const
    {
    return _s;
    }

char CStringCopy::at(size_type idx) const
    {
#ifndef NDEBUG
    assert(idx<size());
#endif
    return _s[idx];
    }

ConcatCharSequence::ConcatCharSequence(
	const CharSequence** seqs,size_t nseq):
	_delegates(new CharSequence*[nseq]),
	_nseqs(nseq),
	length(0)
    {
    std::memcpy(_delegates,seqs,sizeof(CharSequence*)*nseq);
    for(size_t i=0;i< _nseqs;++i)
   	{
	length += _delegates[i]->size();
   	}
    }

ConcatCharSequence::~ConcatCharSequence()
    {
    delete [] _delegates;
    }

ConcatCharSequence::size_type ConcatCharSequence::size() const
    {
    return length;
    }

char ConcatCharSequence::at(size_type idx) const
    {
    for(size_t i=0;i< _nseqs;++i)
    	{
    	size_type n= _delegates[i]->size();
    	if(idx>=n)
    	    {
    	    idx-=n;
    	    }
    	else
    	    {
    	    return _delegates[i]->at(idx);
    	    }
    	}
    throw std::runtime_error("index out of range");
    }
